package io.github.biezhi.wechat.bot;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;
import io.github.biezhi.wechat.WeChatBot;
import io.github.biezhi.wechat.api.annotation.Bind;
import io.github.biezhi.wechat.api.constant.Config;
import io.github.biezhi.wechat.api.enums.AccountType;
import io.github.biezhi.wechat.api.enums.MsgType;
import io.github.biezhi.wechat.api.enums.RedisKeyEnum;
import io.github.biezhi.wechat.api.enums.Word;
import io.github.biezhi.wechat.api.model.TuLinReqModel;
import io.github.biezhi.wechat.api.model.TuLinRspModel;
import io.github.biezhi.wechat.api.model.WeChatMessage;
import io.github.biezhi.wechat.api.model.tulin.Results;
import io.github.biezhi.wechat.utils.*;
import io.github.biezhi.wechat.voice.BaiduVoiceRequest;
import io.github.biezhi.wechat.voice.BaiduVoiceResponse;
import io.github.biezhi.wechat.voice.BaiduVoiceTtsRequest;
import io.github.biezhi.wechat.voice.BaiduVoiceTtsResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;

import javax.activation.MimetypesFileTypeMap;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author wangsh
 * 图灵机器人实现
 * @date 2019-07-01 23:17
 */
@Slf4j
@Component
public class Tulinbot extends WeChatBot {

    public static Set<String> USER_NAME_LIST = new HashSet<>();
    public static Set<String> VOICE_USER_NAME_LIST = new HashSet<>();
    public static List<String> KEY_LIST = new ArrayList<>();

    final static AipSpeech CLIENT = new AipSpeech(BaiduVoiceUtils.APP_ID, BaiduVoiceUtils.API_KEY, BaiduVoiceUtils.SECRET_KEY);

    public static int index = 0;

    @Value("${wechat.autoLogin}")
    private boolean autoLogin;

    @Value("${wechat.showTerminal}")
    private boolean showTerminal;

    @Value("${wechat.assetsDir}")
    private String assetsDir;

    @Value("${wechat.tarVoicePath}")
    private String tarVoicePath;

    @Value("${wechat.ttsVoicePath}")
    private String ttsVoicePath;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Config config;

    static {
        KEY_LIST.add("4132446a628e4c4b9b9e15e9be5a764d");
        KEY_LIST.add("bea137e22cab41bb9be8df8f8178ffa0");
        KEY_LIST.add("d6a5445bf1a74780be454071c182dd02");
        KEY_LIST.add("0383d55c02a24cc6a2d336fe7ead5110");
        KEY_LIST.add("691d650fad6d497f8f12337db049d39f");
    }

    @PostConstruct
    private void construct() {
        config.autoLogin(autoLogin).showTerminal(showTerminal).assetsDir(assetsDir);
    }


    public Tulinbot(Config config) {
        super(config);
    }

    /**
     * 绑定群聊信息
     *
     * @param message
     */
    @Bind(msgType = MsgType.ALL, accountType = AccountType.TYPE_GROUP)
    public void groupMessage(WeChatMessage message) {
        sendToGroup(message);
    }

    /**
     * 发送群消息
     *
     * @param message
     */
    private void sendToGroup(WeChatMessage message) {
        String text = message.getText();
        log.info("收到的消息是=====>" + text);
        String fromUserName = message.getFromUserName();
        //1.收到 开启消息时 回复 是否选择语音回复  1 普通消息 2 语音消息
        String converPath = ttsVoicePath + DateUtils.dateToStr(new Date(), "yyyyMMddHHmmssSSS") + ".mp3";
        if (Word.OPEN_VOICE_CHAT.getType().equals(text)) {
            VOICE_USER_NAME_LIST.add(fromUserName);
            covertText2Voice(Word.OK_MASTER.getType(), converPath, 3);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.api().sendFile(fromUserName, converPath);
            return;
        }
        //1.收到说话字样时  将该username加入list
        if (Word.OPEN_CHAT.getType().equals(text)) {
            log.info("开启机器人模式====>" + text);
            USER_NAME_LIST.add(fromUserName);
            this.api().sendText(fromUserName, Word.OPEN_VOICE_CHAT.getType());
            return;
        }
        //2.收到闭嘴时就不说话  username移除
        if (Word.CLOSE_CHAT.getType().equals(text) || Word.CLOSE_VOICE_CHAT.getType().equals(text)) {
            log.info("关闭机器人模式====>" + text);
            USER_NAME_LIST.remove(fromUserName);
            this.api().sendText(fromUserName, Word.OK.getType());
            return;
        }
        for (String name : USER_NAME_LIST) {
            if (name.equals(fromUserName)) {
                if (MsgType.VOICE.equals(message.getMsgType())) {
                    sendVoice(message, null, null);
                }
                if (MsgType.TEXT.equals(message.getMsgType())) {
                    model(text, fromUserName, KEY_LIST.get(index), null, null);
                }
            }
        }
        for (String name : VOICE_USER_NAME_LIST) {
            if (name.equals(fromUserName)) {
                if (MsgType.VOICE.equals(message.getMsgType())) {
                    //语音消息也用语音回复
                    sendVoice(message, MsgType.VOICE, converPath);
                }
                if (MsgType.TEXT.equals(message.getMsgType())) {
                    //文字消息也有语音回复
                    model(message.getText(), fromUserName, KEY_LIST.get(index), MsgType.VOICE, converPath);
                }
            }
        }
    }


    private void covertText2Voice(String text, String voicePath, Integer per) {
        String token = getToken();
        BaiduVoiceTtsRequest ttsRequest = new BaiduVoiceTtsRequest(text, token, per);
        HashMap<String, Object> map = new HashMap<>();
        map.put("cuid", ttsRequest.getCuid());
        map.put("lan", ttsRequest.getLan());
        map.put("tok", ttsRequest.getTok());
        map.put("aue", ttsRequest.getAue() + "");
        map.put("ctp", ttsRequest.getCtp() + "");
        map.put("per", ttsRequest.getPer() + "");
        map.put("pit", ttsRequest.getPit() + "");
        map.put("spd", ttsRequest.getSpd() + "");
        map.put("vol", ttsRequest.getVol() + "");

        // 调用接口
        TtsResponse res = CLIENT.synthesis(text, "zh", 1, map);
        byte[] data = res.getData();
        org.json.JSONObject res1 = res.getResult();
        if (data != null) {
            try {
                Util.writeBytesToFileSystem(data, voicePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (res1 != null) {
            log.info(res1.toString(2));
        }
    }

    /**
     * 发送给好友
     *
     * @param message
     */
    private void sendFriend(WeChatMessage message, MsgType msgType, String ttsVoicePath) {
        String text = message.getText();
        String fromUserName = message.getFromUserName();
        //如果是语音消息 则读取并且调用百度api识别
        if (MsgType.VOICE.equals(message.getMsgType())) {
            sendVoice(message, msgType, ttsVoicePath);
        }
        if (MsgType.IMAGE.equals(message.getMsgType())) {
            log.info("图片消息地址====>" + message.getImagePath());
        }
        if (MsgType.VIDEO.equals(message.getMsgType())) {
            log.info("视频消息地址====>" + message.getVideoPath());
        }
        if (MsgType.TEXT.equals(message.getMsgType())) {
            model(text, fromUserName, KEY_LIST.get(index), msgType, ttsVoicePath);
        }
    }


    private String getToken() {
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        String token = opsForValue.get(RedisKeyEnum.VOICE_KEY);
        if (StringUtils.isBlank(token)) {
            token = BaiduVoiceUtils.getAccessToken();
            opsForValue.set(RedisKeyEnum.VOICE_KEY, token, 2592000, TimeUnit.SECONDS);
        }
        return token;
    }


    private void sendVoice(WeChatMessage message, MsgType msgType, String ttsVoicePath) {
        log.info("语音消息地址====>" + message.getVoicePath());
        String token = getToken();
        //转换语音
        String tarPath = tarVoicePath + DateUtils.dateToStr(new Date(), "yyyyMMddHHmmssSSS") + ".pcm";
        FFmpegUtils.convertPcmVoice(message.getVoicePath(), tarPath);
        File notExistFile = new File(tarPath);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String encodeVoice = Base64.getEncoder().encodeToString(BinUtils.fileToBinArray(notExistFile));
        //获得请求对象
        BaiduVoiceRequest request = BaiduVoiceUtils.baiduVoiceRequest(token, encodeVoice, (int) notExistFile.length());
        String post = HttpUtils.post(BaiduVoiceUtils.VOICE_SPEED_URL, JSONObject.toJSONString(request));
        BaiduVoiceResponse baiduVoiceResponse = JSONObject.parseObject(post, BaiduVoiceResponse.class);
        if (baiduVoiceResponse.getErr_no() != 0) {
            log.info("转换错误码" + baiduVoiceResponse.getErr_no());
            this.api().sendText(message.getFromUserName(), Word.VOICE_FAILURE.getType());
        } else {
            List<String> result = baiduVoiceResponse.getResult();
            if (result.size() != 0) {
                log.info("语音识别结果:" + result);
                model(result.get(0), message.getFromUserName(), KEY_LIST.get(index), msgType, ttsVoicePath);
            }
        }
    }


    /**
     * 绑定私聊消息
     *
     * @param message
     */
    @Bind(msgType = MsgType.ALL, accountType = AccountType.TYPE_FRIEND)
    public void friendMessage(WeChatMessage message) {
        if (Word.OPEN_CHAT.getType().equals(message.getText())) {
            USER_NAME_LIST.add(message.getFromUserName());
        }
        if (Word.CLOSE_CHAT.getType().equals(message.getText())) {
            USER_NAME_LIST.remove(message.getFromUserName());
        }
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        String chatType = opsForValue.get(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName());
        if (USER_NAME_LIST.contains(message.getFromUserName())) {
            if (StringUtils.isBlank(chatType)) {
                //默认文字
                opsForValue.set(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName(), "1");
            }
            if (Word.CHANGE_CHAT_TYPE.getType().equals(message.getText())) {
                String type = opsForValue.get(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName());
                if ("1".equals(type)) {
                    this.api().sendText(message.getFromUserName(), "聊天方式现已更换为语音");
                    opsForValue.set(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName(), "2");
                }
                if ("2".equals(type)) {
                    this.api().sendText(message.getFromUserName(), "聊天方式现已更换为文字");
                    opsForValue.set(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName(), "1");
                }
                return;
            }
            String nowType = opsForValue.get(RedisKeyEnum.USER_VOICE_KEY + message.getFromUserName());
            if ("1".equals(nowType)) {
                sendFriend(message, MsgType.TEXT, null);
            }
            if ("2".equals(nowType)) {
                String tts = ttsVoicePath + DateUtils.dateToStr(new Date(), "yyyyMMddHHmmssSSS") + ".mp3";
                sendFriend(message, MsgType.VOICE, tts);
            }
        }
    }

    private TuLinRspModel model(String text, String userName, String apiKey, MsgType msgType, String ttsVoicePath) {
        TuLinReqModel reqModel = TuLinBotUtils.createReqModel(text, null, apiKey);
        String jsonString = JSONObject.toJSONString(reqModel);
        String rsp = HttpUtils.post(TuLinBotUtils.TULIN_URL, jsonString);
        TuLinRspModel tuLinRspModel = JSONObject.parseObject(rsp, TuLinRspModel.class);
        List<Results> resultsList = tuLinRspModel.getResults();
        for (Results results : resultsList) {
            String result = results.getValues().get("text").toString();
            if (result.contains(Word.FAILURE.getType())) {
                this.api().sendText(userName, Word.FAILURE_WORD.getType());
                index++;
            } else {
                if (MsgType.TEXT.equals(msgType)) {
                    this.api().sendText(userName, result);
                }
                if (MsgType.VOICE.equals(msgType)) {
                    covertText2Voice(result, ttsVoicePath, 3);
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    this.api().sendFile(userName, ttsVoicePath);
                }
            }
            if (index + 1 == KEY_LIST.size()) {
                index = 0;
            }
            log.info("发送消息" + result);
        }
        return tuLinRspModel;
    }


    /**
     * 好友验证消息
     *
     * @param message
     */
    @Bind(msgType = MsgType.ADD_FRIEND)
    public void addFriend(WeChatMessage message) {
        log.info("收到好友验证消息: {}", message.getText());
        if (message.getText().contains("java")) {
            this.api().verify(message.getRaw().getRecommend());
        }
    }

}
